/* Soubor je ulozen v kodovani UTF-8.
 * Kontrola kódování: Příliš žluťoučký kůň úpěl ďábelské ódy.
 */
package cz.vse.adventura.po1715.xskrz00_skrivankova.gui;

import cz.pecinovsky.english.pojj.adv_framework.game_gui.IListener;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFileChooser;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;



/*******************************************************************************
 * Instance třídy {@code MenuRecord} představují položky hlavní nabídky
 * s příkazy, které ovlivňují vytváření a zobrazování žurnálz příkazů
 * zadávaných hře.
 *
 * @author    Rudolf PECINOVSKÝ
 * @version   0.00.000
 */
@SuppressWarnings("serial")
public class MenuRecord extends JMenu
        implements IListener<String>
{
//== KONSTANTNÍ ATRIBUTY TŘÍDY =================================================
//== PROMĚNNÉ ATRIBUTY TŘÍDY ===================================================
//== STATICKÝ INICIALIZAČNÍ BLOK - STATICKÝ KONSTRUKTOR ========================
//== KONSTANTNÍ ATRIBUTY INSTANCÍ ==============================================

    /** GUI, pro které nabídka pracuje. */
    private final GUI_04 gui;

    /** Seznam zapamatovaných příkazů. */
    private final List<ČasPříkaz> seznamPříkazů = new ArrayList<ČasPříkaz>();

    /** Okno, v němž se bude na požádání vypisovat žurnál zadaných příkazů. */
    private final WindowRecord oknoŽurnálu = new WindowRecord();



//== PROMĚNNÉ ATRIBUTY INSTANCÍ ================================================

    /** Příznak toho, zda se vůbec bude ukládat žurnál. */
    private boolean dělatŽurnál = true;

    /** Příznak zobrazování žurnálu v samostatném okně. */
    private boolean doOkna = false;

    /** Příznak ukládání žurnálu do zadaného souboru. */
    private boolean doSouboru = false;

    /** Soubor, do nějž se ukládá zaznamenávaný žurnál. */
    private File soubor = null;

    /** Proud, do nějž se ukládají zaznamenávaná data. */
    private Writer proud = null;



//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ TŘÍDY ========================================
//== OSTATNÍ NESOUKROMÉ METODY TŘÍDY ===========================================

//##############################################################################
//== KONSTRUKTORY A TOVÁRNÍ METODY =============================================

    /***************************************************************************
     * Vytvoří nabídku, jejímž prostřednictvím bude možno ovlivňovat
     * zaznamenávání příkazů do žurnálu.
     *
     * @param gui Grafické rozhraní, pro něž nabídka pracuje
     */
    public MenuRecord(GUI_04 gui)
    {
        super("Záznam");
        setMnemonic('Z');
        this.gui = gui;

        příkazZaznamenávatPříkazy();
        příkazDoOkna();
        příkazDoSouboru();
        příkazNovýSoubor();
    }



//== ABSTRAKTNÍ METODY =========================================================
//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ INSTANCÍ =====================================
//== OSTATNÍ NESOUKROMÉ METODY INSTANCÍ ========================================

    /***************************************************************************
     * Hlášeni o zadání příkazu, resp. o odstartování nové hry.
     *
     * @param příkaz Zadaný příkaz nebo {@code null} oznamující
     *               zahájení nové hry
     */
    @Override
    public void notice(String příkaz)
    {
        if (! dělatŽurnál) {
            return;
        }
        if (příkaz == null) {
            notice("_____________________________");
            notice("===== Spuštěna nová hra =====");
            oznamAutora();
            return;
        }
        ČasPříkaz záznam = new ČasPříkaz(příkaz);
        seznamPříkazů.add(záznam);
        String zpráva = záznam.toString();
        oknoŽurnálu.připiš(zpráva);
        if (doSouboru) {
            záznamDoSouboru(zpráva);
        }
    }



//== SOUKROMÉ A POMOCNÉ METODY TŘÍDY ===========================================
//== SOUKROMÉ A POMOCNÉ METODY INSTANCÍ ========================================

    /***************************************************************************
     * Zanese do žurnálu zprávu o tom, čí hra se právě hraje.
     */
    private void oznamAutora()
    {
        notice("      Autor: " + gui.getGame().getAuthor());
    }


    /***************************************************************************
     * Nastaví, zda se bude zobrazovat aplikační okno
     * se zaznamenanými příkaz.
     */
    private void příkazDoOkna()
    {
        final JCheckBoxMenuItem zobrazovatVOkně =
                        new JCheckBoxMenuItem("Zobrazovat v okně", false);
        zobrazovatVOkně.setMnemonic('O');
        zobrazovatVOkně.setState(doOkna);
        this.add(zobrazovatVOkně);

        zobrazovatVOkně.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e)
            {
                doOkna = ! doOkna;
                if (doOkna) {
                    oknoŽurnálu.setVisible(true);
                }
            }
        });
    }


    /***************************************************************************
     * Nastaví zaznamenávání žurnálu do souboru.
     * Pokud ještě není nastaven žádný soubor, požádá uživatele o jeho zadání.
     */
    private void příkazDoSouboru()
    {
        JCheckBoxMenuItem zápisDoSouboru =
                        new JCheckBoxMenuItem("Zapisovat do souboru", false);
        zápisDoSouboru.setMnemonic('s');
        zápisDoSouboru.setState(doSouboru);
        this.add(zápisDoSouboru);

        zápisDoSouboru.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e)
            {
                doSouboru = ! doSouboru;
                if (doSouboru) {
                    if (proud == null) {
                        připravProud();
                    } //(proud == null)
                } //(doSouboru)
            }
        });
    }


    /***************************************************************************
     * Připraví povel nabídky, po jehož zadání se zavře případný současný
     * soubor, do nějž se zapisuje žurnál, a umožní uživateli zadat soubor nový.
     */
    private void příkazNovýSoubor()
    {
        JMenuItem novýSoubor = new JMenuItem("Nový soubor");
        novýSoubor.setMnemonic('N');
        this.add(novýSoubor);

        novýSoubor.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if (proud != null) {
                    try {
                        proud.close();
                        proud = null;
                    } catch (IOException ex) {
                        zpráva("\nProblémy při zavírání souboru " + soubor);
                    }
                }
                soubor = null;
                zjistiSoubor();
            }
        });
    }


    /***************************************************************************
     * Nastaví, zda se bude vytvářet žurnál, tj. zda se budou
     * zaznamenávat jednotlvié příkazy spolu s časem, kdy byly zadány.
     */
    private void příkazZaznamenávatPříkazy()
    {
        final JCheckBoxMenuItem zaznamenávatPříkazy =
                        new JCheckBoxMenuItem("Zaznamenávat příkazy", false);
        zaznamenávatPříkazy.setMnemonic('Z');
        zaznamenávatPříkazy.setState(dělatŽurnál);
        this.add(zaznamenávatPříkazy);

        zaznamenávatPříkazy.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e)
            {
                boolean nová = ! dělatŽurnál;
                dělatŽurnál = true;
                if (nová) {
                    notice("+++++ Záznam příkazů zapnut +++++");
                    oznamAutora();
                }
                else {
                    oznamAutora();
                    notice("----- Záznam příkazů vypnut -----");
                }
                dělatŽurnál = nová;
            }
        });
    }


    /***************************************************************************
     * Pokusí se otevřít proud, do nějž se bude zapisovat žurnál;
     * pokud se mu to nepodaří, oznámí to uživateli
     * a zápis žurnálu do souboru vypne.
     */
    private void připravProud()
    {
        if (soubor == null) {
            zjistiSoubor();
            if (soubor == null) {
                doSouboru = false;
                return;
            }
        } //(soubor == null)
        try {
            proud = new BufferedWriter(new FileWriter(soubor));
        } catch (IOException ex) {
            zpráva("Nepodařilo se otevřít výstupní soubor\n\n" + soubor +
               "\n\nZápis žurnálu do souboru není nastaven");
            doSouboru = false;
            proud = null;
        }
    }


    /***************************************************************************
     * Zaznamená zadanou událost do souboru.
     *
     * @param zpráva Zaznamenávaná zpráva
     */
    private void záznamDoSouboru(String zpráva)
    {
        if (proud == null) {
            throw new RuntimeException("\nNení otevřen výstupní proud");
        }
        try {
            proud.write(zpráva);
            proud.flush();
        } catch (IOException ex) {
            zpráva("Při zápisu žurnálu do souboru se vyskytly problémy");
        }
    }


    /***************************************************************************
     * Zjistí jméno souboru, do nějž se má ukládat žurnál.
     */
    private void zjistiSoubor()
    {
        final JFileChooser fc = new JFileChooser();
        int ret = fc.showOpenDialog(gui.getRodiče());
        if (ret == JFileChooser.APPROVE_OPTION) {
            //Pokud uživatel něco zadal, zloží informaci do proměnné žSoubor
            soubor = fc.getSelectedFile();
        }
    }


    /***************************************************************************
     * Vypíše v dialogovém okně zprávu o problémech
     * a počká až uživatel potvrdí, že si ji přečetl.
     *
     * @param text Text zprávy
     */
    private void zpráva(String text)
    {
        JOptionPane.showMessageDialog(gui.getRodiče(), text);
    }



//== INTERNÍ DATOVÉ TYPY =======================================================

    /***************************************************************************
     * Instance třídy {@code ČasPříkaz} slouží jako přepravky usnadňující
     * zapamatování a opětnovnou rekonstrukci průběhu hry.
     */
    private static class ČasPříkaz
    {
        final Date   čas;     //Čas, kdy byl příkaz zadáýní
        final String příkaz;  //Zadaný příkaz


        /***********************************************************************
         * Konstruktor vystaqčí pouze se zadáním příkazu,
         * protože za čas zadání příkazu považuje čas svého zavolání,
         * který si zjistí a uloží.
         *
         * @param příkaz
         */
        ČasPříkaz(String příkaz)
        {
            this.čas    = new Date();
            this.příkaz = příkaz;
        }


        /***********************************************************************
         * Vrátí podpis dané položky sestávající z data a času
         * následovaného textem příkazu.
         *
         * @return Podpis
         */
        @Override
        public String toString()
        {
            String podpis = String.format("%tF - %<tT:  %s\n", čas, příkaz);
            return podpis;
        }


    }



//== TESTY A METODA MAIN =======================================================
//
//    /*************************************************************************
//     * Testovací metoda.
//     */
//    public static void test()
//    {
//        MenuRecord inst = new MenuRecord();
//    }
//    /** @param args Parametry příkazového řádku - nepoužívané. */
//    public static void main( String[] args )  {  test();  }
}
